﻿using ArtMath.Core.Data;
using ArtMath.Core.Helper;
using System;
using System.Collections.Generic;

namespace ArtMath.PointCollection
{
    [Serializable]
    public class PartialCircle : Circle
    {
        public double Start, Sweep;
        public PartialCircle()
        {

        }
        public PartialCircle(PointD center, double r, double start, double sweep) : base(center, r)
        {
            Start = start;
            Sweep = sweep;
        }
        public override PointD? HeadPoint
        {
            get { return Center.Offset(Start, Radius); }
        }
        public override PointD? TailPoint
        {
            get { return Center.Offset(Start + Sweep, Radius); }
        }
        public override bool Contains(PointD pt)
        {
            return Utility.InSweepRange(Start, Sweep, Center.GetAngle(pt)) &&
                base.Contains(pt);
        }
        public override PointD? GetNearestPoint(PointD pt)
        {
            if (!Utility.InSweepRange(Start, Sweep, Center.GetAngle(pt)))
            {
                double angleStart = Utility.GetAngle(Utility.GetUnitVector(Start), pt - Center),
                    angleEnd = Utility.GetAngle(Utility.GetUnitVector(Start + Sweep), pt - Center);
                return Center.Offset(Start + (angleStart < angleEnd ? 0 : Sweep), Radius);
            }
            return base.GetNearestPoint(pt);
        }
        public override double GetSlideExcess(PointD currentLocation, double length)
        {
            double nowAngle = Center.GetAngle(currentLocation);
            if (length > 0)
            {
                return length - Utility.GetSweepAngle(nowAngle, Start + Sweep, Sweep < 0) * Radius;
            }
            else
            {
                return -length - Utility.GetSweepAngle(nowAngle, Start, Sweep >= 0) * Radius;
            }
        }
        public override PointD SlideDestination(PointD currentLocation, double length, out bool reverse)
        {
            reverse = false;
            double arclen = Math.Abs(Sweep) * Radius;
            length %= 2 * arclen;
            double nowAngle = Center.GetAngle(currentLocation),
                toStart = Utility.GetSweepAngle(nowAngle, Start, Sweep >= 0),
                toEnd = Utility.GetSweepAngle(nowAngle, Start + Sweep, Sweep < 0),
                excess = Math.Abs(length) - (length > 0 ? toEnd : toStart) * Radius;
            if (excess <= 0)
            {
                return Center.Offset(nowAngle + Math.Sign(Sweep) * length / Radius, Radius);
            }
            else
            {
                if (length > 0)
                {
                    excess -= arclen;
                    if (excess <= 0)
                    {
                        reverse = true;
                        return Center.Offset(Start - Math.Sign(Sweep) * excess / Radius, Radius);
                    }
                    else
                    {
                        return Center.Offset(Start + Math.Sign(Sweep) * excess / Radius, Radius);
                    }
                }
                else
                {
                    if (excess <= arclen)
                    {
                        reverse = true;
                        return Center.Offset(Start + Math.Sign(Sweep) * excess / Radius, Radius);
                    }
                    else
                    {
                        return Center.Offset(Start + Sweep - Math.Sign(Sweep) * (excess - arclen) / Radius, Radius);
                    }
                }
            }
        }
        protected override PointCollectionBase Intersect(PointCollectionBase collection)
        {
            HashSet<PointD> set = new HashSet<PointD>();
            PartialCircle arc = collection as PartialCircle;
            if (arc != null)
            {
                set.AddRange(CurveIntersection.ArcsIntersection(Center, Radius, Start, Sweep, arc.Center, arc.Radius, arc.Start, arc.Sweep));
            }
            else
            {
                Circle cir = collection as Circle;
                if (cir != null)
                {
                    set.AddRange(CurveIntersection.ArcCircleIntersection(Center, Radius, Start, Sweep, cir.Center, cir.Radius));
                }
            }
            if (set.Count == 0) return Empty.Instance;
            return AfterIntersect(collection.AfterIntersect(new DiscontinuityPoint(set)));
        }
    }
}
